#include <Ogre.h>
#include <CEGUI.h>
#include <CEGUIXMLParser.h>
#include <float.h>


#include "DwarfFort/Interface.h"
#include "DwarfFort/InternalStructures.h"

#include "Main/Logger.h"
#include "Main/App.h"

#include "GUI/DwarfGUI.h"
#include "GUI/Cutaway.h"

#include "Graphics/MapRenderer.h"
#include "Graphics/MapCellMirror.h"
#include "Graphics/MaterialAtlas.h"

#include "Graphics/MapBlock.h"
#include "BatchPage.h"
#include "ImpostorPage.h"

#include "CellDesignations.h"
#include "AsmCall.h"
using namespace Graphics;
using namespace Forests;


MapRenderer::MapRenderer(Ogre::SceneManager *sceneManager,Ogre::Camera *camera)
		:mSceneManager(sceneManager), mCamera(camera),
		mAtlas(0),
		mQueueId(Ogre::RENDER_QUEUE_1),
		StateListener(DwarfFort::GS_INGAME)
{



};



MapRenderer::~MapRenderer()
{


};


void MapRenderer::stateCreated()
{
	DwarfFort::MatGlossList *list = DwarfFort::Interface::getSingleton().getMatGlossList(); 
	mCellList = DwarfFort::Interface::getSingleton().getMapCellList();


	DWORD timer = GetTickCount();

	mAtlas = new MaterialAtlas( list->mStone,"materials-stone.xml");
//	
	


	/*CEGUI::XMLParser *pars = CEGUI::System::getSingleton().getXMLParser();
	MapBlock::XMLHandler handler(mSceneManager,mAtlas);
	pars->parseXMLFile(handler,"cells.xml","MapBlocks.xsd","");
	mMapBlocks = handler.retrieveBlockMap();*/

	mCellList = DwarfFort::Interface::getSingleton().getMapCellList() ;

//	mMirrorList = new MapCellMirrorList(mCellList,mAtlas,mSceneManager);

	
	mAtlas->loadDefinitions();
	loadMeshes();
	preParseMap();

	mAtlas->build();
	


	
	mPagedGeometry = new PagedGeometry(mCamera,Ogre::Vector3(16,2,16));
	int x,y,z;
	x = mCellList->getXSize();
	y = mCellList->getZSize();
	z = mCellList->getYSize();
	
	mPagedGeometry->setBounds(Ogre::AxisAlignedBox(0,0,0,x*16,y*2,z*16));
	mPagedGeometry->addDetailLevel<BatchPage>(100, 0);	
	mPagedGeometry->setPageLoader(this);
	dummy = mSceneManager->createEntity("dummy123", "unknown_block.mesh");
	

	mCutawayLevel = Main::App::getSingleton().getGUI()->getCutawayControler()->getCutawayLevel();
	Main::App::getSingleton().getGUI()->getCutawayControler()->setMaximumCutaway(mCellList->getZSize());

	mSceneManager->showBoundingBoxes(true);
	return;
	
}

void MapRenderer::preParseMap()
{
	int maxX = mCellList->getXSize(); 
	int maxY = mCellList->getYSize(); 
	int maxZ = mCellList->getZSize(); 

	for (int x = 0; x < maxX; x++)
		for (int y = 0; y < maxY; y++)
			for (int z = 0; z < maxZ; z++)
			{
				DwarfFort::MapCell * cell = mCellList->getMapCell(x,y,z);
				if (!cell) continue;
				for (int x1 = 0; x1 < 16; x1++)
					for (int y1 = 0; y1 < 16; y1++)
					{
						unsigned short originalBlock = cell->mBlocks[y1+x1*16];
						if (originalBlock == 0 || originalBlock == 32) continue; // open space
						if (mCellDesignations.find(originalBlock) == mCellDesignations.end()) continue;
						DWORD flags = cell->mFlags1[y1+x1*16];
						WORD mat = 0xFFFF;
						DwarfFort::Vein *vein = 0;
						CellDesignation *designation = mCellDesignations[originalBlock];
						if (designation->DefaultMatgloss[0])
						{
							mat =  mAtlas->getMaterialIndexByName(std::string(designation->DefaultMatgloss));
						}
						if (mat == 0xFFFF && cell->veins.size())
						{
							std::hacked_vector<DwarfFort::Vein *>::iterator iter;
							for (iter = cell->veins.begin(); iter != cell->veins.end(); ++iter)
							{
								AsmCall isVein((*iter)->vf_table[0]);
								if (isVein() == 1)
								{
									vein = (*iter);
									if ((vein->bitMask[y] & (1 << x))==0)
									{
										vein = 0;
									}
								}
							}
						} 
						if (mat == 0xFFFF && vein)
							mat = vein->veinType; 
						if (mat == 0xFFFF)
						{
							DWORD offset = (flags &0x1E0000) >> 17;
							if (offset > 9) _asm int 3;
							char val = cell->layerOffset[offset];
							int ofs_x = (val % 3) - 1;
							int ofs_y = (val / 3) - 1;
							int reg_x = ((x + cell->start_x)/48 + cell->region_x)/16 + ofs_x;
							int reg_y = ((y + cell->start_y)/48 + cell->region_y)/16 + ofs_y;
							// hack 4 now
							DwarfFort::Region ***regions = (DwarfFort::Region ***)(0x015C6A3C);
							DwarfFort::Region &region = (*regions)[reg_x][reg_y];
							std::hacked_vector<DwarfFort::unkStruct2*> *unkvec2 = (std::hacked_vector<DwarfFort::unkStruct2*> *) 0x015C6A0C;
							int defSoil = region.defaultSoil;
							size_t size = unkvec2->size();
							if (defSoil >= size)
							{
								mat = mAtlas->getDefaultMaterialIndex();
								break;
							}
							int something = (flags&0x3c00 )>> 10;
							std::hacked_vector<DwarfFort::unkStruct3*> &unkvec3= unkvec2->at(region.defaultSoil)->unknown;
							size_t size2 = unkvec3.size();
							if (something >= size2)
							{
								mat = mAtlas->getDefaultMaterialIndex();
								break;
							}
							DwarfFort::unkStruct3* s = unkvec3.at(something);
							mat = s->default_material;
						}
						if (mat == 0xFFFF)
							mat = mAtlas->getDefaultMaterialIndex();
					 
						MaterialDef *def = mAtlas->getOrCreateMaterialDef(mat);
						def->useCount++; 
					}
			}

}



void MapRenderer::loadMeshes()
{
	int count = sizeof(gCellDesignations)/ sizeof(CellDesignation);
	for (int i = 0; i < count; i++)
	{
		CellDesignation *designation = gCellDesignations+i;
		mCellDesignations[gCellDesignations[i].id] = designation;
		if (gCellDesignations[i].id == 0 || gCellDesignations[i].id == 32 ) continue;
		if (designation->BaseMeshName[0])
		{
			designation->BaseEntity = mSceneManager->createEntity(StringConverter::toString(designation->id)+"_map_block",
																  designation->BaseMeshName); 
		}
		else 
			designation->BaseEntity = mSceneManager->createEntity(StringConverter::toString(designation->id)+"_map_block",
																  "unknown_block.mesh"); 

	}
}



void MapRenderer::stateRemoved()
{
	if (mAtlas)
		delete mAtlas;
	if (mPagedGeometry)
		delete mPagedGeometry;
}


void MapRenderer::stateUpdate(int level)
{
	unsigned int cutawayLevel = Main::App::getSingleton().getGUI()->getCutawayControler()->getCutawayLevel();
	if (cutawayLevel != mCutawayLevel)
	{
		AxisAlignedBox bounds(mPagedGeometry->get3DBounds());
		Ogre::Real newMaxY = (this->mCellList->getZSize()-cutawayLevel) * 2.0f;
		Ogre::Real oldMaxY = (this->mCellList->getZSize()-mCutawayLevel) * 2.0f;
		bounds.setMaximumY(newMaxY);
		bool show;
		if (cutawayLevel > mCutawayLevel)
		{
			Ogre::Real temp = oldMaxY;
			oldMaxY = newMaxY;
			newMaxY = temp;
			show = false;
		} else
			show = true;
		
		bounds.setMaximumY(newMaxY-0.01f);
		bounds.setMinimumY(oldMaxY+0.01f);
		mPagedGeometry->showGeometryPages(bounds,show);
		mCutawayLevel = cutawayLevel;
	}
	mPagedGeometry->update();
}


 
void MapRenderer::loadPage(PageInfo &page)
{
	
	Ogre::Vector3 bounds = page.bounds.getMinimum();  
	
	DwarfFort::MapCell * cell = mCellList->getMapCell(page.xIndex,page.zIndex,page.yIndex);  
	if (!cell) return;
	for (int x = 0; x < 16; x++)
		for (int y = 0; y < 16; y++)
		{
			unsigned short originalBlock = cell->mBlocks[y+x*16];
			if (!originalBlock || originalBlock == 32) continue;
			DWORD flags = cell->mFlags1[y+x*16];
			if (mCellDesignations.find(originalBlock) == mCellDesignations.end())
				_asm int 3;
			CellDesignation *designation = mCellDesignations[originalBlock];
			if (designation->BaseEntity == 0 )
			{
			  if ( designation->isWall || designation->isFloor || designation->isSlope)
			  {
				this->addEntity(dummy,Ogre::Vector3(bounds.x + x,bounds.y,bounds.z +y),Ogre::Quaternion::IDENTITY);
			  }
			}
			else
			{
				WORD mat = 0xFFFF;
				DwarfFort::Vein *vein = 0;
				if (designation->DefaultMatgloss[0])
				{
					mat =  mAtlas->getMaterialIndexByName(std::string(designation->DefaultMatgloss));
					//_asm int 3;
				}
				if (mat == 0xFFFF && cell->veins.size())
				{
					std::hacked_vector<DwarfFort::Vein *>::iterator iter;
					for (iter = cell->veins.begin(); iter != cell->veins.end(); ++iter)
					{
						AsmCall isVein((*iter)->vf_table[0]);
						if (isVein() == 1)
						{
							vein = (*iter);
							if ((vein->bitMask[y] & (1 << x))==0)
							{
								vein = 0;
							}
						}
					}
				} 
				if (mat == 0xFFFF && vein)
					mat = vein->veinType; 
				if (mat == 0xFFFF)
				{
					DWORD offset = (flags &0x1E0000) >> 17;
					if (offset > 9) _asm int 3;
					char val = cell->layerOffset[offset];
					int ofs_x = (val % 3) - 1;
					int ofs_y = (val / 3) - 1;
					int reg_x = ((x + cell->start_x)/48 + cell->region_x)/16 + ofs_x;
					int reg_y = ((y + cell->start_y)/48 + cell->region_y)/16 + ofs_y;
					// hack 4 now
					DwarfFort::Region ***regions = (DwarfFort::Region ***)(0x015C6A3C);
					DwarfFort::Region &region = (*regions)[reg_x][reg_y];
					std::hacked_vector<DwarfFort::unkStruct2*> *unkvec2 = (std::hacked_vector<DwarfFort::unkStruct2*> *) 0x015C6A0C;
					int defSoil = region.defaultSoil;
					size_t size = unkvec2->size();
					if (defSoil >= size)
					{
						mat = mAtlas->getDefaultMaterialIndex();
						break;
					}
					int something = (flags&0x3c00 )>> 10;
					std::hacked_vector<DwarfFort::unkStruct3*> &unkvec3= unkvec2->at(region.defaultSoil)->unknown;
					size_t size2 = unkvec3.size();
					if (something >= size2)
					{
						mat = mAtlas->getDefaultMaterialIndex();
						break;
					}
					DwarfFort::unkStruct3* s = unkvec3.at(something);
					mat = s->default_material;
				}else 
					mat = mAtlas->getDefaultMaterialIndex();
				MaterialDef *def = mAtlas->getMaterialDef(mat);
				 
				if (!def)
					def = mAtlas->getMaterialDef(mAtlas->getDefaultMaterialIndex());

				designation->BaseEntity->setMaterialName(mAtlas->getBaseMaterial()->getName()); 
				Vector2 scale = Vector2(def->w,def->h);
				Vector2 offset = Vector2(def->u,def->v);
				this->addEntity(designation->BaseEntity,
								Vector3(bounds.x + x,bounds.y,bounds.z +y),
								Quaternion(Degree(designation->Rotation),Vector3(0,1,0)),
								Vector3::UNIT_SCALE,ColourValue::White,
								offset,scale);
			}
		}
	
}

